#ifndef OPENCV_COMPAT_HPP_
#define OPENCV_COMPAT_HPP_
#include <opencv/cv.h>
#include <opencv/highgui.h>
#include <iostream>
#include "components.hpp"

namespace quadil{
namespace cv{
	inline static IplImage* __convertLabelToColor__(Image<t_label> &img){
		IplImage*res=cvCreateImage(cvSize(img.width,img.height),IPL_DEPTH_8U,3);
		t_label* curRowIn;
		t_label cur_lab;
		unsigned char* curPixelOut;
		int x,y;
		for(y=0;y<img.height;y++){
			curRowIn=img.getRow(y);
			curPixelOut=(unsigned char*)(res->imageData+y*res->widthStep);
			for(x=0;x<img.width;x++){
				if(curRowIn[x]){
					cur_lab=curRowIn[x];
					cur_lab=cur_lab%65536;///if there are labels > 2^16 there might be colision
					curPixelOut[0]=(0!=(cur_lab&(1<<0)))*128+
							(0!=(cur_lab&(1<<2)))*64+
							(0!=(cur_lab&(1<<4)))*32+
							(0!=(cur_lab&(1<<6)))*16+
							(0!=(cur_lab&(1<<8)))*8+
							(0!=(cur_lab&(1<<10)))*4+
							(0!=(cur_lab&(1<<12)))*2+
							(0!=(cur_lab&(1<<14)));
					curPixelOut[1]=255;
					curPixelOut[2]=(0!=(cur_lab&(1<<1)))*128+
							(0!=(cur_lab&(1<<3)))*64+
							(0!=(cur_lab&(1<<5)))*32+
							(0!=(cur_lab&(1<<7)))*16+
							(0!=(cur_lab&(1<<9)))*8+
							(0!=(cur_lab&(1<<11)))*4+
							(0!=(cur_lab&(1<<13)))*2+
							(0!=(cur_lab&(1<<15)));
				}else{
					curPixelOut[0]=0;
					curPixelOut[1]=0;
					curPixelOut[2]=0;
				}
				curPixelOut+=3;
			}
		}
		return res;
	}
	inline static Image<t_label> __convertColorToLabel__(IplImage* iplimg){
		Image<t_label> res(iplimg->width,iplimg->height);
		t_label* curRowOut;
		t_label cur_lab;
		unsigned char ch1;
		unsigned char ch2;
		unsigned char* curPixelIn;
		int x,y;
		for(y=0;y<res.height;y++){
			curRowOut=res.getRow(y);
			curPixelIn=(unsigned char*)(iplimg->imageData+y*iplimg->widthStep);
			for(x=0;x<res.width;x++){
				if(curPixelIn[0]+curPixelIn[1]+curPixelIn[2]!=0){
					ch1=curPixelIn[0];
					ch2=curPixelIn[2];
					cur_lab=((ch1&128)!=0)*1+
							((ch1&64)!=0)*4+
							((ch1&32)!=0)*16+
							((ch1&16)!=0)*64+
							((ch1&8)!=0)*256+
							((ch1&4)!=0)*1024+
							((ch1&2)!=0)*4096+
							((ch1&1)!=0)*16384+
							((ch2&128)!=0)*2+
							((ch2&64)!=0)*8+
							((ch2&32)!=0)*32+
							((ch2&16)!=0)*128+
							((ch2&8)!=0)*512+
							((ch2&4)!=0)*2048+
							((ch2&2)!=0)*8192+
							((ch2&1)!=0)*32768;
					curRowOut[x]=cur_lab;
				}else{
					curRowOut[x]=0;
				}
				curPixelIn+=3;
			}
		}
		return res;
	}
	inline static IplImage __cvwrap__(Image<t_byte> &img){
		IplImage res;
		res.nSize=sizeof(IplImage);
		res.nChannels=1;
		res.dataOrder=0;
		res.origin=0;
		res.roi=NULL;
		res.maskROI=NULL;
		res.imageId=NULL;
		res.imageId=NULL;
		res.tileInfo=NULL;
		res.width=img.width;
		res.height=img.height;
		res.depth=IPL_DEPTH_8U;
		res.ID=0;
		res.imageData=(char*)(img.fp);
		res.widthStep=img.lineStride*sizeof(t_byte);
		res.imageSize=res.height*res.widthStep;
		res.imageDataOrigin=0;
		return res;
	}
	inline Image<t_byte> loadByteImage(std::string fname){
		IplImage* tmp=cvLoadImage(fname.c_str(),0);
		if(!tmp || (tmp->nChannels!=1)||(tmp->depth!=IPL_DEPTH_8U && tmp->depth!=IPL_DEPTH_8S)){
			throw "quadil::cv::loadByteImage input image is not of required depth and chanels, expected 1 chanels* 8 bit.";
		}
		Image<t_byte> res(tmp->width,tmp->height);
		IplImage wraper=__cvwrap__(res);
		cvCopy(tmp,&wraper);
		cvReleaseImage(&tmp);
		return res;
	}
	inline int saveByteImage(std::string fname,Image<t_byte>& img){
		IplImage wraper=__cvwrap__(img);
		return cvSaveImage(fname.c_str(),&img);
	}
	inline void drawByteImage(std::string wname,Image<t_byte>& img){
		IplImage wraper=__cvwrap__(img);
		cvShowImage(wname.c_str(),&wraper);
	}
	inline int saveLabelImage(Image<t_label>& image,std::string fname){
		IplImage* tmp=__convertLabelToColor__(image);
		int res=cvSaveImage(fname.c_str(),tmp);
		cvReleaseImage(&tmp);
		return res;
	}
	inline void showLabelImage(Image<t_label>& image,std::string wname){
		IplImage* tmp=__convertLabelToColor__(image);
		cvShowImage(wname.c_str(),tmp);
		cvReleaseImage(&tmp);
	}
	inline Image<t_label> loadLabelImage(std::string fname){
		IplImage* tmp=cvLoadImage(fname.c_str(),1);
		if(!tmp || (tmp->nChannels!=3)||(tmp->depth!=IPL_DEPTH_8U && tmp->depth!=IPL_DEPTH_8S)){
			throw "quadil::cv::loadLabelImage input image is not of required depth and chanels, expected 3 chanels* 8 bit.";
		}
		Image<t_label>res=__convertColorToLabel__(tmp);
		cvReleaseImage(&tmp);
		return res;
	}
	class ComponentSetViewer{
		std::string component_winname;
		std::string image_winname;
		std::string zoom_winname;
		std::vector<quadil::ComponentPointer> components;
		ComponentPointer curComponent;
		IplImage* totalImage;
		int imgWidth;
		int imgHeight;
		int curComponentNum;
		void showComponent(int num){
			//std::cerr<<"showComponent 1:comp num="<<num<<" nbComponents="<<components.size()<<"\n";
			std::cerr.flush();
			if(num<0){
				num=components.size()+num;
			}
			num=num % components.size();
			//std::cerr<<"showComponent 2:comp num="<<num<<" nbComponents="<<components.size()<<"\n";
			std::cerr.flush();
			curComponentNum=num;
			curComponent=components[num];
			CvRect comproi1=cvRect(curComponent->getLeft()-1,0,curComponent->getWidth()+1,imgHeight-1);
			CvRect comproi2=cvRect(0,curComponent->getTop(),imgWidth-1,curComponent->getHeight());
			CvRect zoomroi=cvRect(curComponent->getLeft()-100,curComponent->getTop()-100,curComponent->getWidth()+200,curComponent->getHeight()+200);
			CvRect comproi=cvRect(curComponent->getLeft()-1,curComponent->getTop(),curComponent->getWidth()+1,curComponent->getHeight());
			if(zoomroi.width+zoomroi.x>=imgWidth){
				zoomroi.width=totalImage->width-1;
			}
			if(zoomroi.height+zoomroi.y>=imgHeight){
				zoomroi.height=totalImage->height-1;
			}
			if(zoomroi.x<0){
				zoomroi.x=0;
			}
			if(zoomroi.y<0){
				zoomroi.y=0;
			}
			if(comproi.height<2){
				comproi.height=2;
			}
			if(comproi.width<2){
				comproi.width=2;
			}

			//std::cerr<<"showComponent 3:comp num="<<num<<" cvRect=(x="<<comproi.x<<",y="<<comproi.y<<",w="<<comproi.width<<",h="<<comproi.height<<")"<<""<<"\n";
			cvSetImageROI(totalImage,comproi1);
			cvNot(totalImage,totalImage);
			cvResetImageROI(totalImage);
			cvSetImageROI(totalImage,comproi2);
			cvNot(totalImage,totalImage);
			cvResetImageROI(totalImage);

			cvShowImage(image_winname.c_str(),totalImage);
			cvSetImageROI(totalImage,comproi);
			cvShowImage(component_winname.c_str(),totalImage);
			cvResetImageROI(totalImage);
			cvSetImageROI(totalImage,zoomroi);
			cvShowImage(zoom_winname.c_str(),totalImage);
			cvResetImageROI(totalImage);
			cvSetImageROI(totalImage,comproi1);
			cvNot(totalImage,totalImage);
			cvResetImageROI(totalImage);
			cvSetImageROI(totalImage,comproi2);
			cvNot(totalImage,totalImage);
			cvResetImageROI(totalImage);
		}
	public:
		ComponentSetViewer(ComponentSet & c,const char* imgwn="Image",const char* cmpwnm="Component",const char* zwnm="Zoom",int imgw=1000,int imgh=1000,int cmpw=500,int cmph=400)
			:image_winname(imgwn),component_winname(cmpwnm),zoom_winname(zwnm){
			cvNamedWindow(image_winname.c_str(),0);
			cvNamedWindow(component_winname.c_str(),0);
			cvNamedWindow(zoom_winname.c_str(),0);
			cvMoveWindow(image_winname.c_str(),50,50);
			cvMoveWindow(component_winname.c_str(),100+imgw,50);
			cvMoveWindow(zoom_winname.c_str(),100+imgw,100+cmph);

			cvResizeWindow(image_winname.c_str(),imgw,imgh);
			cvResizeWindow(component_winname.c_str(),cmpw,cmph);
			cvResizeWindow(zoom_winname.c_str(),cmpw,cmph);
			totalImage=__convertLabelToColor__(c);
			imgWidth=totalImage->width;
			imgHeight=totalImage->height;
			c.getComponents(this->components);
			curComponentNum=0;
			showComponent(curComponentNum);
			std::cerr<<"ComponentSetSize="<<c.width<<","<<c.height<<"\n";
		}
		void guiLoop(){
			int key;
			int end=0;
			do{
				key=cvWaitKey(1);
				key=cvWaitKey(0);
				switch(key){
				case 'n':curComponentNum++;
						 showComponent(curComponentNum);
						 break;
				case 'p':curComponentNum--;
						 showComponent(curComponentNum);
						 break;
				case 'f':curComponentNum=0;
						 showComponent(curComponentNum);
						 break;
				case 'q':
						end=1;
						break;
				}
			}while(!end);
		}
		~ComponentSetViewer(){
			cvReleaseImage(&this->totalImage);
			cvDestroyWindow(image_winname.c_str());
			cvDestroyWindow(component_winname.c_str());
		}
	};
}
}
#endif /*OPENCV_COMPAT_HPP_*/
